package top.misspro.valheim.service;

import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import top.misspro.valheim.model.CommandResultModel;
import top.misspro.valheim.model.ServerInfoModel;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Evan
 */
@Service
public class SSHService {

    private final ConcurrentHashMap<String, ArrayDeque<String>> channelMap = new ConcurrentHashMap<>();
    public final ConcurrentHashMap<String, Thread> threadMap = new ConcurrentHashMap<>();

    /**
     * 为远程SSH发送执行命令
     *
     * @param port     端口
     * @param host     地址
     * @param user     用户
     * @param password 密码
     */
    public void postCommand(int port, String host, String user, String password, String command) {
        JSch jSch = new JSch();
        try {
            Session session = jSch.getSession(user, host, port);
            session.setPassword(password);
            session.setConfig("StrictHostKeyChecking", "no");
            session.setTimeout(600);
            session.connect();

            ChannelExec exec = (ChannelExec) session.openChannel("exec");
            InputStream inputStream = exec.getInputStream();
            exec.setCommand(command);
            exec.connect();

            InputStream in = exec.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in, Charset.forName("UTF-8")));
            String buf = null;
            //所有执行结果，留待日志记录
            StringBuffer sb = new StringBuffer();
            while ((buf = reader.readLine()) != null) {
                sb.append(buf);
                //将执行结果命令行放入队列
                this.addCommandResult(host, buf);
            }
            reader.close();
            exec.disconnect();
            session.disconnect();
        } catch (JSchException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void addCommandResult(String ip, String str) {
        ArrayDeque<String> deque;
        if (this.channelMap.containsKey(ip)) {
            deque = this.channelMap.get(ip);
        } else {
            deque = new ArrayDeque<>();
        }
        deque.addLast(str);
    }

    /**
     * 根据服务器IP获取此服务器执行日志
     * 注意！在获取队列中的命令行时，执行线程也可能同时在写入，注意逻辑
     *
     * @param ip
     * @return
     */
    public CommandResultModel getCommandResultByIP(String ip) {

        //判断是否存在此ip服务器的执行命令结果
        if (this.channelMap.containsKey(ip)) {
            ArrayList<String> results = new ArrayList<>();
            //判断执行线程是否执行完毕，如果是则获取全部
            if (this.threadMap.get(ip).getState() == Thread.State.WAITING || this.threadMap.get(ip).getState() == Thread.State.TERMINATED) {
                //如果执行线程已经完毕，返回全部命令
                results.addAll(this.channelMap.get(ip));
                //释放资源
                this.channelMap.get(ip).clear();
                this.channelMap.remove(ip);
                this.threadMap.remove(ip);

                return CommandResultModel.done(results);
            }

            //如果代码进入此处，这说明执行线程还在执行

            //每次拿100个结果
            for (int i = 0; i < 100; i++) {
                try {
                    results.add(this.channelMap.get(ip).removeFirst());
                } catch (NoSuchElementException e) {
                    break;
                }
            }

            return CommandResultModel.runningResult(results);
        }
        return CommandResultModel.abnormal();
    }

    /**
     * 安装valheim
     * 异步方法
     *
     * @param serverInfoModel
     */
    @Async
    public void installValheim(ServerInfoModel serverInfoModel) {

        Thread thread = Thread.currentThread();
        //保存运行此服务器的线程
        this.threadMap.put(serverInfoModel.getIp(), thread);
        //创建运行此操作的命令结果队列
        this.channelMap.put(serverInfoModel.getIp(), new ArrayDeque<>());
        //服务器执行脚本
        String command = "curl -s https://gitee.com/kuallt/valheim-auto-install/raw/master/valheim_install.sh -o valheim_install.sh&&bash valheim_install.sh&& rm valheim_install.sh";
        //TODO 模拟脚本,测试用
        String test = "yum install tomcat -y && yum remove tomcat -y";
        //发送命令
        postCommand(serverInfoModel.getPort(), serverInfoModel.getIp(), serverInfoModel.getUser(), serverInfoModel.getPassword(), test);
    }
}
